package com.ulwx.database.dbpool;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;
import javax.transaction.Status;
import javax.transaction.UserTransaction;

import org.apache.log4j.Logger;
import org.enhydra.jdbc.pool.StandardXAPoolDataSource;
import org.enhydra.jdbc.standard.StandardXADataSource;
import org.objectweb.jotm.Jotm;
import org.objectweb.transaction.jta.TMService;

import com.jolbox.bonecp.BoneCPDataSource;
import com.mchange.v2.c3p0.DataSources;
import com.ulwx.database.DataBase;
import com.ulwx.database.DbException;
import com.ulwx.tool.CTime;
import com.ulwx.tool.DateTime;
import com.ulwx.tool.RandomUtils;
import com.ulwx.tool.StringUtils;
import com.ulwx.type.TResult2;

public class DBPoolFactory {
	private  static final Logger log = Logger.getLogger(DBPoolFactory.class);
	static class PoolType {
		public static String C3P0 = "c3p0";
		public static String BoneCP = "bonecp";
		public static String jtom = "jtom";
	};

	private   volatile int ExceptionPeriod = CheckSlaveThread.getSleepTime() + 60;// 秒
	
	private   volatile Map<String, DataSource> poollist = new ConcurrentHashMap<String, DataSource>();

	/**
	 * 主库和从库的关系
	 */
	private  volatile Map<String, Map<String, DataSource>> poolSlaveList = new ConcurrentHashMap<String, Map<String, DataSource>>();

	private  volatile Map<String, TResult2<Map<String, DataSource>, TMService>> dispoolist = new ConcurrentHashMap<String, TResult2<Map<String, DataSource>, TMService>>();

	private static volatile DBPoolFactory dbpoolFactory = new DBPoolFactory();

	private  volatile Map<String, Map<String, Date>> poolSlaveExceptionTimes = new ConcurrentHashMap<String, Map<String, Date>>();

	private  volatile Map<String, Map<String, CheckSlaveThread>> poolSlaveExceptionTimesThreads = new ConcurrentHashMap<String, Map<String, CheckSlaveThread>>();

	public  Map<String, Map<String, Date>> getPoolSlaveExceptionTimes() {
		return poolSlaveExceptionTimes;
	}

	public  void setPoolSlaveExceptionTimes(
			Map<String, Map<String, Date>> poolSlaveExceptionTimes) {
		this.poolSlaveExceptionTimes = poolSlaveExceptionTimes;
	}

	public  void setSlaveExceptionTime(String poolName, String serverName) {
		if (poolSlaveList.containsKey(poolName)) {
			Map<String, Date> slaveException = getPoolSlaveExceptionTimes()
					.get(poolName);
			slaveException.put(serverName, CTime.getCurrentDateTime());
		}
	}

	public  Date getSlaveExceptionTime(String poolName, String serverName) {
		if (poolSlaveList.containsKey(poolName)) {
			Map<String, Date> slaveException = getPoolSlaveExceptionTimes()
					.get(poolName);
			return slaveException.get(serverName);
		}
		return null;
	}



	private DBPoolFactory() {
		//System.out.println(poolSlaveExceptionTimes);
		initPool();
	}

	public static DBPoolFactory getInstance() {
		if(dbpoolFactory==null) dbpoolFactory=new DBPoolFactory();
		return dbpoolFactory;
	}

	/**
	 * 向连接池列表添加一个新的池化（Pooled）数据源
	 * 
	 * @param key
	 * @param configFile
	 *            配置文件名。为了简化参数输入与便于外部维护，可以把配置好的文件传入这里分析
	 */
	public void add(String key, String configFile) {
		// read config from file, and build a dbpool
	}

	public boolean isMainSlaveMode(String poolName) {
		if (poolSlaveList != null && poolSlaveList.get(poolName) != null
				&& poolSlaveList.get(poolName).size() > 0)
			return true;

		return false;
	}

	public void setDispoollist() throws DbException {

		dispoolist.clear();
		Map<String, String[]> transMaps = ReadConfig.getInstance().getTrans();
		// log.debug("---"+C)
		Map<String, Map<String, String>> maps = ReadConfig.getInstance()
				.getProperties();
		Set<String> sets = transMaps.keySet();
		Iterator<String> iter = sets.iterator();
		while (iter.hasNext()) {
			try {
				String transName = iter.next();

				TMService jotm = new Jotm(true, false);

				String[] poolnames = transMaps.get(transName);
				TResult2<Map<String, DataSource>, TMService> t2 = new TResult2<Map<String, DataSource>, TMService>();
				t2.setSecondValue(jotm);
				Map<String, DataSource> dm = new HashMap<String, DataSource>();
				t2.setFirstValue(dm);

				dispoolist.put(transName, t2);

				for (int i = 0; i < poolnames.length; i++) {
					String poolName = poolnames[i];
					Map<String, String> map = maps.get(poolName);

					String driverClassName = map.get("driverClassName");
					String url = map.get("url");
					String user = map.get("username");
					String password = map.get("password");
					String checkoutTimeout = StringUtils.trim(
							map.get("checkoutTimeout"), "5000");// 从连接池取连接的最大等待时间，默认5秒
					// String idleConnectionTestPeriod =
					// map.get("idleConnectionTestPeriod");

					String maxIdleTime = StringUtils.trim(
							map.get("maxIdleTime"), "600000");// 默认空隙10分钟后回收
					String maxPoolSize = StringUtils.trim(
							map.get("maxPoolSize"), "30");// 默认30个
					String minPoolSize = StringUtils.trim(
							map.get("minPoolSize"), "20");// 默认20个

					String initialPoolSize = minPoolSize;// 开始的初始化连接数,默认20个
					StandardXADataSource xads = new StandardXADataSource();

					xads.setUrl(url);
					xads.setDriverName(driverClassName);
					xads.setUser(user);
					xads.setPassword(password);

					StandardXAPoolDataSource spds = new StandardXAPoolDataSource(
							Integer.valueOf(initialPoolSize));
					spds.setMaxSize(Integer.valueOf(maxPoolSize));
					spds.setMinSize(Integer.valueOf(minPoolSize));
					spds.setDeadLockMaxWait(Integer.valueOf(checkoutTimeout));
					spds.setLifeTime(Integer.valueOf(maxIdleTime));

					spds.setUser(user);
					spds.setPassword(password);
					/**
					 * sleepTime是对Connection idle检测线程PoolKeeper的检测时间间隔设置。
					 * PoolKeeper会定时监测是否存在超过lifeTime的connection然后释放掉这些connection
					 **/
					spds.setSleepTime(360 * 1000);// PoolKeeper检测时间间隔,360秒
					spds.setDeadLockRetryWait(3 * 1000);// 重试的时间间隔,3秒

					spds.setTransactionManager(jotm.getTransactionManager());
					spds.setDataSource(xads);

					dm.put(poolName, spds);
				}

			} catch (Exception e) {
				throw new DbException(e);
			}
		}
	}

	public void setPoollist() throws DbException {

		try {
			poollist.clear();
			poolSlaveList.clear();
			Map<String, Map<String, String>> maps = ReadConfig.getInstance()
					.getProperties();
			Map<String, Map<String, Map<String, String>>> slaveProperites = ReadConfig
					.getInstance().getSlaveProperites();
			Set<String> sets = maps.keySet();
			Iterator<String> iter = sets.iterator();
			while (iter.hasNext()) {
				try {

					String poolName = iter.next();
					Map<String, String> map = maps.get(poolName);
					Map<String, Map<String, String>> slaveServer = slaveProperites
							.get(poolName);
					String driverClassName = map.get("driverClassName");
					String url = map.get("url");
					String user = map.get("username");
					String password = map.get("password");

					String maxStatements = StringUtils.trim(
							map.get("maxStatements"), "30");
					String checkoutTimeout = StringUtils.trim(
							map.get("checkoutTimeout"), "60000");// 60000毫秒
					String idleConnectionTestPeriod = StringUtils.trim(
							map.get("idleConnectionTestPeriod"), "60");// 40秒
					String type = StringUtils.trim(map.get("type"),
							PoolType.C3P0);
					String maxIdleTime = StringUtils.trim(
							map.get("maxIdleTime"), "600");// 以秒为单位，默认空隙600秒后回收
					String maxPoolSize = StringUtils.trim(
							map.get("maxPoolSize"), "30");// 默认30个
					String minPoolSize = StringUtils.trim(
							map.get("minPoolSize"), "10");// 默认20个
					// String maxStatements =map.get("maxStatements");
					String onlyForDis = StringUtils.trim(map.get("onlyForDis"),
							"").toLowerCase();

					Class.forName(driverClassName);

					if (onlyForDis.equals("true")) {
						continue;
					}
					DataSource ds = null;
					Map<String, DataSource> slaveDataSources = new HashMap<String, DataSource>();

					if (type.equals(PoolType.C3P0)) {

						ds = getDataSourceFromC3p0(url, user, password,
								checkoutTimeout, maxPoolSize, minPoolSize,
								maxStatements, maxIdleTime,
								idleConnectionTestPeriod);
						// 判断ds是否可以获得连接
						boolean res = DBPoolFactory.startDataSource(ds);
						if (!res) {
							continue;
						}
						// 设置从数据库
						if (slaveServer != null) {

							slaveDataSources = getSlaveServerConfig(
									slaveServer, PoolType.C3P0);
						}

					} else if (type.equals(PoolType.BoneCP)) {

						ds = getDataSourceFromBonecp(url, user, password,
								checkoutTimeout, maxPoolSize, minPoolSize,
								maxStatements, maxIdleTime,
								idleConnectionTestPeriod);
						// 判断ds是否可以获得连接
						boolean res = DBPoolFactory.startDataSource(ds);
						if (!res) {
							continue;
						}
						slaveDataSources = getSlaveServerConfig(slaveServer,
								PoolType.BoneCP);
						// 设置从数据库
					}
					// ds.setXXXX(...); // (other config options here)

					if (poollist.containsKey(poolName)) {
						if (type == PoolType.BoneCP) {
							BoneCPDataSource old_pooled = (BoneCPDataSource) poollist
									.get(poolName);
							old_pooled.close();

						} else if (type == PoolType.C3P0) {
							DataSource old_pooled = poollist.get(poolName);
							DataSources.destroy(old_pooled);
						}
						poollist.remove(poolName);
					}

					if (poolSlaveList.containsKey(poolName)) {
						if (type == PoolType.BoneCP) {
							Map<String, DataSource> slaveDss = poolSlaveList
									.get(poolName);
							for (String ssName : slaveDss.keySet()) {
								BoneCPDataSource old_pooled = (BoneCPDataSource) slaveDss
										.get(ssName);
								old_pooled.close();

							}

						} else if (type == PoolType.C3P0) {
							Map<String, DataSource> slaveDss = poolSlaveList
									.get(poolName);
							for (String ssName : slaveDss.keySet()) {
								DataSource old_pooled = slaveDss.get(ssName);
								DataSources.destroy(old_pooled);
							}

						}
						poollist.remove(poolName);
					}
					poollist.put(poolName, ds);
					poolSlaveList.put(poolName, slaveDataSources);
				} catch (Exception ex) {
					// TODO Auto-generated catch block
					log.error("", ex);
					throw ex;
				}
			}

		} catch (Exception e) {
			// log.error("", e);
			new DbException("add database pool error!", e);
		}

	}

	public Map<String, DataSource> getSlaveServerConfig(
			Map<String, Map<String, String>> slaveServer, String poolType)
			throws Exception {

		Map<String, DataSource> slaveDataSources = new HashMap<String, DataSource>();
		if (slaveServer != null) {
			for (String slaveServerName : slaveServer.keySet()) {
				Map<String, String> slaveConfig = slaveServer
						.get(slaveServerName);

				String s_url = slaveConfig.get("url");
				String s_user = slaveConfig.get("username");
				String s_password = slaveConfig.get("password");

				String s_maxStatements = StringUtils.trim(
						slaveConfig.get("maxStatements"), "30");
				String s_checkoutTimeout = StringUtils.trim(
						slaveConfig.get("checkoutTimeout"), "60000");// 60000毫秒
				String s_idleConnectionTestPeriod = StringUtils.trim(
						slaveConfig.get("idleConnectionTestPeriod"), "60");// 40秒
				String s_maxIdleTime = StringUtils.trim(
						slaveConfig.get("maxIdleTime"), "600");// 以秒为单位，默认空隙600秒后回收
				String s_maxPoolSize = StringUtils.trim(
						slaveConfig.get("maxPoolSize"), "30");// 默认30个
				String s_minPoolSize = StringUtils.trim(
						slaveConfig.get("minPoolSize"), "10");// 默认20个

				DataSource s_ds = null;
				if (poolType.equals(PoolType.C3P0)) {
					s_ds = getDataSourceFromC3p0(s_url, s_user, s_password,
							s_checkoutTimeout, s_maxPoolSize, s_minPoolSize,
							s_maxStatements, s_maxIdleTime,
							s_idleConnectionTestPeriod);
				} else if (poolType.equals(PoolType.BoneCP)) {
					s_ds = getDataSourceFromBonecp(s_url, s_user, s_password,
							s_checkoutTimeout, s_maxPoolSize, s_minPoolSize,
							s_maxStatements, s_maxIdleTime,
							s_idleConnectionTestPeriod);
				} else {
					continue;
				}
				// 判断ds是否可以获得连接
				boolean res = DBPoolFactory.startDataSource(s_ds);
				if (!res) {
					if (poolType.equals(PoolType.C3P0)) {
						BoneCPDataSource old_pooled = (BoneCPDataSource) s_ds;
						old_pooled.close();

					} else if (poolType.equals(PoolType.BoneCP)) {
						DataSource old_pooled = s_ds;
						DataSources.destroy(old_pooled);
					}
					continue;
				} else {
					slaveDataSources.put(slaveServerName, s_ds);
				}
			}
		}

		return slaveDataSources;
	}

	public DataSource getDataSourceFromC3p0(String url, String user,
			String password, String checkoutTimeout, String maxPoolSize,
			String minPoolSize, String maxStatements, String maxIdleTime,
			String idleConnectionTestPeriod) throws Exception {

		DataSource ds_unpooled = DataSources.unpooledDataSource(url, user,
				password);

		Map<String, String> config = new HashMap<String, String>();
		config.put("checkoutTimeout", checkoutTimeout);// 1分钟
		config.put("maxPoolSize", maxPoolSize);
		config.put("minPoolSize", minPoolSize);
		config.put("maxStatements", maxStatements);
		config.put("maxIdleTime", maxIdleTime);
		config.put("initialPoolSize", minPoolSize);
		config.put("idleConnectionTestPeriod", idleConnectionTestPeriod);
		DataSource ds_pooled = DataSources
				.pooledDataSource(ds_unpooled, config);
		DataSource ds = ds_pooled;
		return ds;

	}

	public DataSource getDataSourceFromBonecp(String url, String user,
			String password, String checkoutTimeout, String maxPoolSize,
			String minPoolSize, String maxStatements, String maxIdleTime,
			String idleConnectionTestPeriod) throws Exception {

		// load the DB driver
		BoneCPDataSource dataSource = new BoneCPDataSource();
		// dataSource.setLogStatementsEnabled(false);
		dataSource.setJdbcUrl(url); // set the JDBC url
		dataSource.setUsername(user); // set the username
		dataSource.setPassword(password); // set the password
		// dataSource.setIdleMaxAge((long)maxage);//
		int max = Integer.valueOf(maxPoolSize);
		if (max < 2)
			max = 2;
		int min = Integer.valueOf(minPoolSize);
		int idle = Integer.valueOf(maxIdleTime);
		// dataSource.setReleaseHelperThreads(0);
		// dataSource.setAcquireRetryDelay(acquireRetryDelay);
		dataSource.setPartitionCount(1);
		dataSource.setAcquireIncrement(1);
		dataSource.setAcquireRetryDelayInMs(Integer.valueOf(checkoutTimeout));
		dataSource.setMaxConnectionsPerPartition(max);
		dataSource.setMinConnectionsPerPartition(min);
		dataSource.setLogStatementsEnabled(false);
		int test = Integer.valueOf(idleConnectionTestPeriod);// 秒
		dataSource.setIdleConnectionTestPeriod((long) test, TimeUnit.SECONDS);
		dataSource.setIdleMaxAge((long) idle, TimeUnit.SECONDS);
		int statment = Integer.valueOf(maxStatements);
		dataSource.setStatementsCacheSize(statment);
		dataSource.setLazyInit(false);
		dataSource.setDisableJMX(true);

		dataSource.setConnectionTimeoutInMs(10000);// 8秒超时

		DataSource ds = dataSource;
		return ds;

		// 从库的设置

	}

	public static boolean startDataSource(DataSource ds) {
		DataBase db = new DataBase();
		try {
			db.connectDb(ds, false);
		} catch (Exception e) {
			log.error("", e);
			return false;
		} finally {
			if (db != null) {
				try {
					db.close();
				} catch (Exception ex) {
					log.error("", ex);
				}
			}
		}
		return true;
	}

	/**
	 * 向连接池列表添加一个新的池化（Pooled）数据源
	 * 
	 * @param key
	 * @param driver
	 *            数据连接驱动，如com.mysql.jdbc.Driver
	 * @param url
	 *            连接串， 如jdbc:mysql://localhost/samland
	 * @param username
	 *            数据库连接用户名，如samland
	 * @param password
	 *            数据库连接用户密码
	 * @throws Exception
	 */
	synchronized public  void initPool() {

		try {

			this.setPoollist();
			this.setDispoollist();
			// 初始化poolSlaveLiveTimes
			for (String poolName : poolSlaveList.keySet()) {
				Map<String, Date> curmap = new ConcurrentHashMap<String, Date>();
				Map<String, CheckSlaveThread> slaveThreads = new ConcurrentHashMap<String, CheckSlaveThread>();
				log.debug("@@@"+poolSlaveExceptionTimes);
				poolSlaveExceptionTimes.put(poolName, curmap);
				Map<String, DataSource> dss = poolSlaveList.get(poolName);
				/*
				for (String serverName : dss.keySet()) {

					curmap.put(serverName, new Date(0));
					// 启动监控线程，用于监控从数据库是否可连接
					CheckSlaveThread thr = new CheckSlaveThread(poolName,
							serverName, dss.get(serverName));
					slaveThreads.put(serverName, thr);
					thr.start();
				}*/

				poolSlaveExceptionTimesThreads.put(poolName, slaveThreads);

			}

		} catch (Exception e) {
			log.error("", e);
			new Exception("add database pool error!", e);
		}
	}

	synchronized public static void init() {
		DBPoolFactory.getInstance();
	}

	/**
	 * 
	 * @param key
	 *            连接池名
	 * @param pros
	 *            返回的属性
	 * @return
	 * @throws DbException
	 */
	public DataSource getDBPool(String key, Map<String, String> pros)
			throws DbException {
		try {
			Map<String, Map<String, String>> maps = ReadConfig.getInstance()
					.getProperties();
			if (pros != null) {
				pros.putAll(maps.get(key));
			}

			if (key == null)
				throw new DbException("DBPool 'key' CANNOT be null");
			DataSource ds = (DataSource) poollist.get(key);

			return ds;
		} catch (Exception e) {
			throw new DbException(e);
		}
	}

	/**
	 * 根据连接池的名称选择从库
	 * 
	 * @param poolName
	 * @return
	 */
	public DataSource getSlaveDbPool(String poolName) {
		Map<String, DataSource> slaveDss = poolSlaveList
				.get(poolName);
//		Map<String, Date> slaveExceptionTimes = poolSlaveExceptionTimes
//				.get(poolName);

		List<DataSource> availableDss = new ArrayList<DataSource>();
		// 选取
		for (String slaveServerName : slaveDss.keySet()) {
			// 判断当前时间是否
//			Date exceptionTime = slaveExceptionTimes.get(slaveServerName);
//			Date curDateTime = CTime.getCurrentDateTime();
//			
//			long diff = curDateTime.getTime() - exceptionTime.getTime();
//			if (diff > ExceptionPeriod * 1000) {// 表明是安全的数据源
//				availableDss.add(slaveDss.get(slaveServerName));
//			}
			availableDss.add(slaveDss.get(slaveServerName));
		}

		// 随机选一条
		if (availableDss.size() > 0) {
			int index = RandomUtils.nextInt(availableDss.size());
			log.debug("slave server.size:" + availableDss.size() + ",select["
					+ index + "]");
			return availableDss.get(index);
		}
		return null;

	}

	public static void main(String[] args) {
		System.out.println(RandomUtils.nextInt(2));
	}

	public TResult2<Map<String, DataSource>, TMService> get(String transName) {
		TResult2<Map<String, DataSource>, TMService> t2 = dispoolist
				.get(transName);
		return t2;
	}

	public DataSource getDBPool(String transName, String poolName,
			Map<String, String> pros) throws DbException {
		try {
			Map<String, Map<String, String>> maps = ReadConfig.getInstance()
					.getProperties();
			if (pros != null) {
				pros.putAll(maps.get(poolName));
			}

			if (poolName == null)
				throw new DbException("DBPool 'poolName' CANNOT be null");
			TResult2<Map<String, DataSource>, TMService> t2 = dispoolist
					.get(transName);
			Map<String, DataSource> poollist = t2.getFirstValue();
			DataSource ds = (DataSource) poollist.get(poolName);

			return ds;
		} catch (Exception e) {
			throw new DbException(e);
		}
	}

	synchronized public  void clearAll() throws DbException {
		try {
			// 清空常规连接池
			Set<String> sets = poollist.keySet();
			Iterator<String> iter = sets.iterator();
			while (iter.hasNext()) {
				String poolName = iter.next();
				DataSource ds = poollist.get(poolName);
				if (ds instanceof BoneCPDataSource) {
					BoneCPDataSource old_pooled = (BoneCPDataSource) ds;
					old_pooled.close();
					DataSources.destroy(old_pooled);
				} else {
					DataSources.destroy(ds);
				}

			}
			poollist.clear();
			// 停止从库的监听线程
			for (String poolName : poolSlaveExceptionTimesThreads.keySet()) {
				Map<String, CheckSlaveThread> slaveThreads = poolSlaveExceptionTimesThreads
						.get(poolName);
				for (String slaveServerName : slaveThreads.keySet()) {
					CheckSlaveThread thd = slaveThreads.get(slaveServerName);
					// 停止
					thd.stopThread();
					// 等待线程关闭：
					while (thd.isAlive()) {
						try{
						Thread.sleep(100);
						}catch(Exception e){
							
						}
					}
				}
			}

			poolSlaveExceptionTimesThreads.clear();
			// 清除从库的连接池
			for (String poolName : poolSlaveList.keySet()) {
				Map<String, DataSource> slaveMap = poolSlaveList.get(poolName);
				for (String slaveServerName : slaveMap.keySet()) {
					DataSource dss = slaveMap.get(slaveServerName);
					if (dss instanceof BoneCPDataSource) {
						BoneCPDataSource old_pooled = (BoneCPDataSource) dss;
						old_pooled.close();

					} else {
						DataSources.destroy(dss);
					}
				}
			}
			poolSlaveList.clear();
			poolSlaveExceptionTimes.clear();
			// 清除分布式连接池
			Set<String> sets2 = dispoolist.keySet();
			Iterator<String> iter2 = sets2.iterator();
			while (iter2.hasNext()) {
				String transName = iter2.next();
				TResult2<Map<String, DataSource>, TMService> t2 = dispoolist
						.get(transName);
				Map<String, DataSource> poollist = t2.getFirstValue();
				TMService jotm = t2.getSecondValue();
				UserTransaction ut = jotm.getUserTransaction();
				// System.out.println(ut.getStatus());
				if (ut.getStatus() == Status.STATUS_ACTIVE)// 只开始了事务，但未回滚或提交。
				{
					jotm.getUserTransaction().rollback();
				}
				jotm.stop();
				Set<String> poolKeys = poollist.keySet();
				Iterator<String> keyIter = poolKeys.iterator();
				while (keyIter.hasNext()) {
					String poolName = keyIter.next();
					DataSource ds = (DataSource) poollist.get(poolName);
					StandardXAPoolDataSource old_pooled = (StandardXAPoolDataSource) poollist
							.get(poolName);
					old_pooled.stopPool();
				}
				poollist.clear();

			}
			dispoolist.clear();

		} catch (Exception e) {
			throw new DbException("clear database pool error!", e);
		}
	}

}
